package org.jetlinks.pro.auth.service;

import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.crud.events.EntityDeletedEvent;
import org.hswebframework.web.crud.service.GenericReactiveTreeSupportCrudService;
import org.hswebframework.web.id.IDGenerator;
import org.hswebframework.web.system.authorization.api.entity.DimensionUserEntity;
import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionUserService;
import org.jetlinks.pro.auth.entity.OrganizationEntity;
import org.jetlinks.pro.auth.entity.TenantMemberEntity;
import org.jetlinks.pro.auth.tenant.OrganizationAssetType;
import org.jetlinks.pro.authorize.OrgDimensionType;
import org.jetlinks.pro.tenant.TenantAssetManager;
import org.jetlinks.pro.tenant.impl.BindAssetsRequest;
import org.jetlinks.pro.tenant.impl.UnbindAssetsRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class OrganizationService extends GenericReactiveTreeSupportCrudService<OrganizationEntity, String> {

    private DefaultDimensionUserService dimensionUserService;

    private TenantMemberService memberService;

    private TenantAssetManager assetManager;

    @Override
    public IDGenerator<String> getIDGenerator() {
        return IDGenerator.UUID;
    }

    @Override
    public void setChildren(OrganizationEntity entity, List<OrganizationEntity> children) {
        entity.setChildren(children);
    }

    @Transactional
    public Mono<Integer> bindUser(String orgId, List<String> userIdList, Collection<String> tenants) {
        Flux<String> userIdStream;

        if (CollectionUtils.isNotEmpty(tenants)) {
            //如果指定了租户,则只获取租户里有的用户ID,防止修改不是指定租户内的用户信息
            userIdStream = memberService
                .createQuery()
                .where()
                .nest()
                /**/.each(tenants, (query, tenantId) -> query.or().is(TenantMemberEntity::getTenantId, tenantId))
                .end()
                .in(TenantMemberEntity::getUserId, userIdList)
                .fetch()
                .flatMap(member -> assetManager
                    .bindAssetsOwner(member.getTenantId(),
                                     OrganizationAssetType.organization,
                                     member.getUserId(),
                                     Collections.singleton(orgId)
                    )
                    .thenReturn(member.getUserId())
                );
        } else {
            //非租户用户绑定成员时,自动绑定给对应的租户。
            userIdStream = memberService
                .createQuery()
                .where()
                .in(TenantMemberEntity::getUserId, userIdList)
                .fetch()
                .groupBy(TenantMemberEntity::getTenantId)
                .flatMap(tenantGroup -> {
                    String tenantId = String.valueOf(tenantGroup.key());
                    return assetManager.bindAssets(tenantId, false, tenantGroup
                        .map(member -> {
                            BindAssetsRequest request = new BindAssetsRequest();
                            request.setUserId(member.getUserId());
                            request.setAllPermission(true);
                            request.setAssetType(OrganizationAssetType.organization.getId());
                            request.setAssetIdList(Collections.singletonList(orgId));
                            return request;
                        }));
                })
                .thenMany(Flux.fromIterable(userIdList));
        }
        return this
            .findById(orgId)
            .flatMap(org -> userIdStream
                .map(userId -> {
                    DimensionUserEntity userEntity = new DimensionUserEntity();
                    userEntity.setUserId(userId);
                    userEntity.setUserName(userId);
                    userEntity.setDimensionId(orgId);
                    userEntity.setDimensionTypeId(OrgDimensionType.org.getId());
                    userEntity.setDimensionName(org.getName());
                    return userEntity;
                })
                .as(dimensionUserService::save))
            .map(SaveResult::getTotal);

    }

    @Transactional
    public Mono<Integer> unbindUser(String orgId, List<String> userIdList, Collection<String> tenants) {
        Flux<String> userIdStream;

        if (CollectionUtils.isNotEmpty(tenants)) {
            //如果指定里租户,则只获取租户里有的用户ID,防止修改不是指定租户内的用户信息
            userIdStream = memberService
                .createQuery()
                .where()
                .nest()
                /**/.each(tenants, (query, tenantId) -> query.or().is(TenantMemberEntity::getTenantId, tenantId))
                .end()
                .in(TenantMemberEntity::getUserId, userIdList)
                .fetch()
                .flatMap(member -> assetManager
                    .unbindAssetsOwner(member.getTenantId(),
                                       OrganizationAssetType.organization,
                                       member.getUserId(),
                                       Collections.singleton(orgId)
                    )
                    .thenReturn(member.getUserId())
                );
        } else {
            //非租户用户绑定成员时,自动绑定给对应的租户。
            userIdStream = memberService
                .createQuery()
                .where()
                .in(TenantMemberEntity::getUserId, userIdList)
                .fetch()
                .groupBy(TenantMemberEntity::getTenantId)
                .flatMap(tenantGroup -> {
                    String tenantId = String.valueOf(tenantGroup.key());
                    return assetManager
                        .unbindAssets(tenantId, tenantGroup
                            .map(member -> {
                                UnbindAssetsRequest request = new UnbindAssetsRequest();
                                request.setUserId(member.getUserId());
                                request.setAssetType(OrganizationAssetType.organization.getId());
                                request.setAssetIdList(Collections.singletonList(orgId));
                                return request;
                            }));
                })
                .thenMany(Flux.fromIterable(userIdList));
        }
        return userIdStream
            .collectList()
            .filter(CollectionUtils::isNotEmpty)
            .flatMap(newUserIdList -> dimensionUserService
                .createDelete()
                .where(DimensionUserEntity::getDimensionTypeId, OrgDimensionType.org.getId())
                .in(DimensionUserEntity::getUserId, newUserIdList)
                .and(DimensionUserEntity::getDimensionId, orgId)
                .execute())
            ;
    }

    @EventListener
    public void handleDeleteEvent(EntityDeletedEvent<OrganizationEntity> event) {

        List<String> idList = event
            .getEntity()
            .stream()
            .map(OrganizationEntity::getId)
            .collect(Collectors.toList());
        //删除时,解绑维度
        event.async(
            dimensionUserService
                .createDelete()
                .where(DimensionUserEntity::getDimensionTypeId, OrgDimensionType.org.getId())
                .in(DimensionUserEntity::getDimensionId, idList)
                .execute()
        );

    }
}
